
#include <bsp_usart.h>
#include <gd32f10x.h>
#include <rtthread.h>

static uint8_t debug_uart_tx_buffer[DEBUG_UART_TX_BUFFER_SIZE];
static uint8_t debug_uart_rx_buffer[DEBUG_UART_RX_BUFFER_SIZE];
static volatile uint8_t debug_uart_rx_byte = 0;
static volatile uint8_t debug_uart_rd_pos  = 0;
static struct rt_semaphore debug_uart_rx_sem;
#ifdef RT_USING_DEVICE
static rt_device_t debug_uart = RT_NULL;
#endif
static void debug_uart_tx_dma_init(void)
{
    dma_parameter_struct dma_init_struct;

    /* enable DMA0 */
    rcu_periph_clock_enable(RCU_DMA0);

    /* deinitialize DMA channel6(USART1 tx) */
    dma_deinit(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL);
    dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr  = (uint32_t)debug_uart_tx_buffer;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number       = 0;
    dma_init_struct.periph_addr  = (uint32_t) & (USART_DATA(DEBUG_UART));
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL);
    dma_memory_to_memory_disable(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL);
    dma_channel_enable(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL);
}

static void debug_uart_rx_dma_init(void)
{
    dma_parameter_struct dma_init_struct;

    /* enable DMA0 */
    rcu_periph_clock_enable(RCU_DMA0);

    /* debug uart choose idle + DMA to finish data receive */
    dma_deinit(DEBUG_USART_DMA, DEBUG_UART_RX_DMA_CHANNEL);
    dma_init_struct.direction    = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr  = (uint32_t)debug_uart_rx_buffer;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number       = DEBUG_UART_RX_BUFFER_SIZE;
    dma_init_struct.periph_addr  = (uint32_t) & (USART_DATA(DEBUG_UART));
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;

    dma_init(DEBUG_USART_DMA, DEBUG_UART_RX_DMA_CHANNEL, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DEBUG_USART_DMA, DEBUG_UART_RX_DMA_CHANNEL);
    dma_memory_to_memory_disable(DEBUG_USART_DMA, DEBUG_UART_RX_DMA_CHANNEL);

    dma_channel_enable(DEBUG_USART_DMA, DEBUG_UART_RX_DMA_CHANNEL);
}

void debug_uart_dma_config(void)
{
#ifdef DEBUG_UART_DMA_TX
    debug_uart_tx_dma_init();
#endif
#ifdef DEBUBG_UART_IDLE_DMA_RX
    debug_uart_rx_dma_init();
#endif
}

void rt_hw_console_output(const char *str)
{
#ifdef DEBUG_UART_DMA_TX
    /* wait previous dma done */
    while (RESET == dma_flag_get(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL, DMA_FLAG_FTF) &&
           (dma_transfer_number_get(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL) > 0)) {
    }
    rt_size_t size = rt_strlen(str);

    dma_channel_disable(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL);

    dma_flag_clear(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL, DMA_FLAG_FTF);

    rt_memcpy(debug_uart_tx_buffer, str, size);
    debug_uart_tx_buffer[size]     = '\r';
    debug_uart_tx_buffer[size + 1] = '\n';

    dma_transfer_number_config(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL, size + 2);

    dma_channel_enable(DEBUG_USART_DMA, DEBUG_UART_TX_DMA_CHANNEL);

#else

    rt_size_t i = 0, size = 0;
    char a = '\r';
    size   = rt_strlen(str);

    for (i = 0; i < size; i++) {
        if (*(str + i) == '\n') {
            usart_data_transmit(DEBUG_UART, (uint8_t)a);
            while (RESET == usart_flag_get(DEBUG_UART, USART_FLAG_TBE));
        }
        usart_data_transmit(DEBUG_UART, *(str + i));
        while (RESET == usart_flag_get(DEBUG_UART, USART_FLAG_TBE));
    }
#endif
}

// /// @brief open debug uart tx interrupt
// /// @param
// static void open_debug_uart_tx_interrupt(void)
// {
//     nvic_irq_enable(DEBUG_UART_IRQ, 1, 0);

//     usart_interrupt_enable(DEBUG_UART, USART_INT_FLAG_TBE);
// }

/// @brief open debug uart rx interrupt, receive 1 byte or idle
/// @param
static void open_debug_uart_rx_interrupt(void)
{
    nvic_priority_group_set(NVIC_PRIGROUP_PRE3_SUB1);
    nvic_irq_enable(DEBUG_UART_IRQ, 1, 0);

#ifdef DEBUBG_UART_IDLE_DMA_RX
    usart_interrupt_enable(DEBUG_UART, USART_INT_FLAG_IDLE); /* idle interrupt */
#else
    usart_interrupt_enable(DEBUG_UART, USART_INT_RBNE); /* receive interrupt */
#endif
}

/// @brief debug uart initialization
/// @param
/// @return
int debug_uart_init(void)
{
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_AF);
    rcu_periph_clock_enable(DEBUG_RCU);

    gpio_init(DEBUG_PIN_GROUP, GPIO_MODE_AF_PP, GPIO_OSPEED_10MHZ, DEBUG_TX_PIN);
    gpio_init(DEBUG_PIN_GROUP, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_10MHZ, DEBUG_RX_PIN);

    /* disable USART */
    usart_disable(DEBUG_UART);

    usart_deinit(DEBUG_UART);

    /* configure USART baud rate value */
    usart_baudrate_set(DEBUG_UART, 115200);

    /* configure USART parity function */
    usart_parity_config(DEBUG_UART, USART_PM_NONE);

    /* configure USART word length */
    usart_word_length_set(DEBUG_UART, USART_WL_8BIT);

    /* configure USART stop bit length */
    usart_stop_bit_set(DEBUG_UART, USART_STB_1BIT);

    /* configure USART transmitter */
    usart_transmit_config(DEBUG_UART, USART_TRANSMIT_ENABLE);

    /* configure USART receiver */
    usart_receive_config(DEBUG_UART, USART_RECEIVE_ENABLE);

    /* disable hardware flow control */
    usart_hardware_flow_rts_config(DEBUG_UART, USART_RTS_DISABLE);
    usart_hardware_flow_cts_config(DEBUG_UART, USART_CTS_DISABLE);

    open_debug_uart_rx_interrupt();

    /* enable USART */
    usart_enable(DEBUG_UART);

#ifdef DEBUG_UART_DMA_TX
    /* use DMA */
    usart_dma_transmit_config(DEBUG_UART, USART_DENT_ENABLE);
#ifdef DEBUBG_UART_IDLE_DMA_RX
    usart_dma_receive_config(DEBUG_UART, USART_DENR_ENABLE);
#endif
#endif

    /* enable debug uart idle interrupt for receive shell cmd */
    rt_sem_init(&debug_uart_rx_sem, "debug_rx_sem", 0, RT_IPC_FLAG_PRIO);
    return 0;
}

void DEBUG_UART_HANDLER(void)
{
    rt_interrupt_enter();

#ifdef DEBUBG_UART_IDLE_DMA_RX
    if (RESET != usart_interrupt_flag_get(DEBUG_UART, USART_INT_FLAG_IDLE)) {

        /* clear USART_INT_FLAG_IDLE */
        usart_data_receive(DEBUG_UART);

        /* disable RX DMA_Channel */
        dma_channel_disable(DEBUG_USART_DMA, DEBUG_UART_RX_DMA_CHANNEL);

        /* get DMA total receive bytes */
        debug_uart_rx_byte = DEBUG_UART_RX_BUFFER_SIZE - dma_transfer_number_get(DEBUG_USART_DMA, DEBUG_UART_RX_DMA_CHANNEL);
        rt_sem_release(&debug_uart_rx_sem);

        /* reset DMA_Channel CNT */
        dma_transfer_number_config(DEBUG_USART_DMA, DEBUG_UART_RX_DMA_CHANNEL, DEBUG_UART_RX_BUFFER_SIZE);
    }

#else

    if (RESET != usart_interrupt_flag_get(DEBUG_UART, USART_INT_FLAG_RBNE)) {

        usart_interrupt_flag_clear(DEBUG_UART, USART_INT_FLAG_RBNE);

        debug_uart_rx_buffer[debug_uart_rx_byte] = (uint8_t)usart_data_receive(DEBUG_UART);

        /* receive 1 byte */
        if (debug_uart_rx_byte == 0xff) {
            debug_uart_rx_byte = 0;
        } else {
            debug_uart_rx_byte++;
        }

        rt_sem_release(&debug_uart_rx_sem);
    }

#endif
    rt_interrupt_leave();
}

char rt_hw_console_getchar(void)
{
    int ch = -1;

    /* nothing to read */
    if (debug_uart_rx_byte == debug_uart_rd_pos) {
        debug_uart_rx_byte = 0;
        debug_uart_rd_pos  = 0;

#ifdef DEBUBG_UART_IDLE_DMA_RX
        rt_sem_take(&debug_uart_rx_sem, RT_WAITING_FOREVER);

        /* enable RX DMA_Channel again to receive new cmd */
        dma_channel_enable(DEBUG_USART_DMA, DEBUG_UART_RX_DMA_CHANNEL);
#endif
        return ch;
    }

    /* remain bytes to read */
#ifndef DEBUBG_UART_IDLE_DMA_RX
    rt_sem_take(&debug_uart_rx_sem, RT_WAITING_FOREVER);
#endif

    ch = debug_uart_rx_buffer[debug_uart_rd_pos];
    debug_uart_rd_pos++;

    return ch;
}

#ifdef RT_USING_DEVICE
int init_debug_uart_device(void)
{
    debug_uart = rt_device_create(RT_Device_Class_Char, 0);
    if (debug_uart == RT_NULL) {
        return RT_ENOMEM;
    }
    return RT_EOK;
}

static rt_err_t device_init(rt_device_t dev)
{
    if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED)) {
        dev->flag |= RT_DEVICE_FLAG_ACTIVATED;

        /* debug uart should keep RT_DEVICE_FLAG_RDWR flag */
        RT_ASSERT(dev->flag & RT_DEVICE_FLAG_RDWR);

        /* debug uart should keep STREAM output */
        if (!(dev->flag & RT_DEVICE_FLAG_STREAM)) {
            dev->flag |= RT_DEVICE_FLAG_STREAM;
        }

        /* debug uart receives data with byte receive interrupt mode */
        if (dev->flag & RT_DEVICE_FLAG_INT_RX) {
#ifdef DEBUBG_UART_IDLE_DMA_RX
#undef DEBUBG_UART_IDLE_DMA_RX
#endif
            /* make sure open receive RBNE interrupt */
            open_debug_uart_rx_interrupt();
        }
        /* debug uart receives data with fram receive idle + DMA mode */
        else if (dev->flag & RT_DEVICE_FLAG_DMA_RX) {
#ifndef DEBUBG_UART_IDLE_DMA_RX
#define DEBUBG_UART_IDLE_DMA_RX
#endif
            /* make sure open receive IDLE interrupt */
            open_debug_uart_rx_interrupt();
            debug_uart_rx_dma_init();
        }
        /* debug uart transmit data with DMA */
        if (dev->flag & RT_DEVICE_FLAG_DMA_TX) {
            debug_uart_tx_dma_init();
        }
        /* debug uart transmit data with data tranmit done interrupt */
        else if (dev->flag & RT_DEVICE_FLAG_INT_TX) {
            open_debug_uart_tx_interrupt();
        }
    }

    return RT_EOK;
}

static rt_err_t device_open(rt_device_t dev, rt_uint16_t open_flag)
{
    /* debug uart not open, open it */
    if (!(dev->open_flag & RT_DEVICE_OFLAG_OPEN)) {
        dev->open_flag |= RT_DEVICE_OFLAG_OPEN;

        /* debug uart should keep RT_DEVICE_FLAG_RDWR flag */
        RT_ASSERT(dev->open_flag & RT_DEVICE_OFLAG_RDWR);

        usart_enable(DEBUG_UART);
    }
    return RT_EOK;
}

static rt_err_t device_close(rt_device_t dev)
{
    /* debug uart is open, close it */
    if ((dev->open_flag != RT_DEVICE_OFLAG_CLOSE)) {
        dev->open_flag = RT_DEVICE_OFLAG_CLOSE;

        usart_disable(DEBUG_UART);
    }
    return RT_EOK;
}

static rt_err_t device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    if (dev->flag & RT_DEVICE_FLAG_INT_RX) {
    }
    return RT_EOK;
}

static rt_err_t device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    return size;
}

static rt_err_t device_control(rt_device_t dev, int cmd, void *args)
{
    return RT_EOK;
}

// static struct rt_device_ops debug_uart_ops = {
//     device_init,
//     device_open,
//     device_close,
//     device_read,
//     device_write,
//     device_control};

#endif

int esp12f_uart_init(void)
{
    rcu_periph_clock_enable(ESP12F_GPIO_RCU);

    gpio_init(ESP12F_PIN_GROUP, GPIO_MODE_OUT_OD, GPIO_OSPEED_10MHZ, ESP12F_TX_PIN);
    gpio_init(ESP12F_PIN_GROUP, GPIO_MODE_IPU, GPIO_OSPEED_10MHZ, ESP12F_RX_PIN);
    return 0;
}